﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Sharp.Interprocess.MemoryManager
{
    internal static class TreeExtensions
    {
        internal static bool TryFindMultipleNodes(this GeneralTree<SegmentBlock> segmentBlockData,
                                                  Func<SegmentBlock,bool> searchPredicate,
                                                  int blockCountToFind,
                                                  out IEnumerable<GeneralTree<SegmentBlock>> foundSegmentBlocks)
        {
            bool hasFound = false;
            foundSegmentBlocks = null;
            List<GeneralTree<SegmentBlock>> foundData = new List<GeneralTree<SegmentBlock>>();
            for (int blockIndex = 0; blockIndex < blockCountToFind; blockIndex++)
            {
                var foundSegmentBlock = segmentBlockData.FindNode(segment => searchPredicate(segment) && 
                                                                             !foundData.Any(foundSegmentNode => foundSegmentNode.Data.Equals(segment)));
                if (foundSegmentBlock == null)
                {
                    break;
                }
                else
                {
                    foundData.Add(foundSegmentBlock);
                }
            }

            if (foundData.Count == blockCountToFind)
            {
                hasFound = true;
                foundSegmentBlocks = foundData;
            }

            return hasFound;
        }

        internal static GeneralTree<SegmentBlock> FindNodeAndSplit(this GeneralTree<SegmentBlock> segmentBlockData, 
                                                                   int allocationSize,
                                                                   HashSet<long> allocatedSegmentsBySizeAndOffset)
        {
            if (segmentBlockData.Data.BlockSize == allocationSize)
            {
                return segmentBlockData;
            }

            if(segmentBlockData.Data.BlockSize < allocationSize)
            {
                return null;
            }

            foreach (var segmentBlockNode in segmentBlockData.ChildNodes)
            {
                var foundNode = FindNodeAndSplit(segmentBlockNode, allocationSize, allocatedSegmentsBySizeAndOffset);
                if (foundNode != null)
                {
                    return foundNode;
                }
            }

            return null;
        }

        internal static IEnumerable<GeneralTree<T>> GetChildNodes<T, TKey>(this GeneralTree<T> tree, Func<GeneralTree<T>, bool> nodeFilter, Func<GeneralTree<T>, TKey> orderByKeySelector)
            where T : IEquatable<T>
        {
            return tree.ChildNodes
                       .Where(nodeFilter)
                       .OrderBy(orderByKeySelector);
        }

    }
}
